package rahmath.sharif.docrypt;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;

import rahmath.sharif.docrypt.constant.DocryptConstants;
import rahmath.sharif.docrypt.constant.EncodingType;
import rahmath.sharif.docrypt.util.BaseCoder;
import rahmath.sharif.docrypt.util.DocryptUtil;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.support.v4.app.NavUtils;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

public class FileCodeActivity extends Activity implements DocryptConstants {

	private String inputFilePath;
	private String destinationDirectory;
	private ProgressDialog progressDialog;

	@TargetApi(11)
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_file_code);
		Spinner spinner = (Spinner) findViewById(R.id.spinnerEncodingMethods);
		ArrayAdapter<EncodingType> spinnerAdapter = new ArrayAdapter<EncodingType>(
				this, android.R.layout.simple_spinner_item,
				EncodingType.values());
		spinnerAdapter.setDropDownViewResource(R.layout.spinner_dropdown);
		spinner.setAdapter(spinnerAdapter);
		if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB) {
			getActionBar().setDisplayHomeAsUpEnabled(true);
		}
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.activity_file_code, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case android.R.id.home:
			NavUtils.navigateUpFromSameTask(this);
			return true;
		case R.id.help:
			Intent helpActivity = new Intent(this, HelpActivity.class);
			startActivity(helpActivity);
			return true;
		}
		return super.onOptionsItemSelected(item);
	}

	public void chooseFile(View view) {
		Intent intentChooseFile = new Intent(this, Explorer.class);
		intentChooseFile.putExtra(INTENT_EXTRA_CHOOSE, FILE);
		try {
			intentChooseFile
					.putExtra(INTENT_EXTRA_FILE_PATH,
							inputFilePath == null ? Environment
									.getExternalStorageDirectory()
									.getCanonicalPath() : new File(
									inputFilePath).getParentFile().getPath());
		} catch (IOException e) {
			e.printStackTrace();
		}
		startActivityForResult(intentChooseFile, INTENT_CHOOSE_FILE);
	}

	public void chooseDirectory(View view) {
		Intent intentChooseFile = new Intent(this, Explorer.class);
		intentChooseFile.putExtra(INTENT_EXTRA_CHOOSE, DIRECTORY);
		try {
			intentChooseFile.putExtra(INTENT_EXTRA_FILE_PATH,
					destinationDirectory == null ? Environment
							.getExternalStorageDirectory().getCanonicalPath()
							: destinationDirectory);
		} catch (IOException e) {
			e.printStackTrace();
		}
		startActivityForResult(intentChooseFile, INTENT_CHOOSE_DIRECTORY);
	}

	@Override
	public void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);
		switch (requestCode) {
		case INTENT_CHOOSE_FILE:
			if (resultCode == Activity.RESULT_OK) {
				inputFilePath = data.getStringExtra(INTENT_EXTRA_CHOOSEN_PATH);
				((TextView) findViewById(R.id.editTextInputFile))
						.setText(inputFilePath);
			}
			break;
		case INTENT_CHOOSE_DIRECTORY:
			if (resultCode == Activity.RESULT_OK) {
				destinationDirectory = data
						.getStringExtra(INTENT_EXTRA_CHOOSEN_PATH);
				((TextView) findViewById(R.id.editTextDestinationDirectory))
						.setText(destinationDirectory);
			}
			break;
		}
		if (null != inputFilePath && 0 != inputFilePath.length()
				&& null != destinationDirectory
				&& 0 != destinationDirectory.length()) {
			findViewById(R.id.btnEncode).setEnabled(true);
			findViewById(R.id.btnDecode).setEnabled(true);
		} else {
			findViewById(R.id.btnEncode).setEnabled(false);
			findViewById(R.id.btnDecode).setEnabled(false);
		}
	}

	public void encode(View view) {
		String sourcePath = ((EditText) findViewById(R.id.editTextInputFile))
				.getText().toString();
		String derivedPath = ((EditText) findViewById(R.id.editTextDestinationDirectory))
				.getText().toString();
		if (null == sourcePath || null == derivedPath
				|| sourcePath.length() == 0 || derivedPath.length() == 0) {
			Toast.makeText(getApplicationContext(),
					getString(R.string.chooseSrcDests), Toast.LENGTH_SHORT)
					.show();
			return;
		}
		File sourceFile = new File(sourcePath);
		if ((sourceFile.length() * (long) 2) > DocryptUtil.getSpaceAvailable()) {
			Toast.makeText(getApplicationContext(),
					getString(R.string.spaceNotAvailable), Toast.LENGTH_SHORT)
					.show();
			return;
		}
		File derivedFile = new File(derivedPath + FILE_SEPARATOR
				+ sourceFile.getName() + ".txt");
		EncodingType encodingType = EncodingType
				.forName(((Spinner) findViewById(R.id.spinnerEncodingMethods))
						.getSelectedItem().toString());
		final Docode docode = new Docode(sourceFile, derivedFile, encodingType,
				true);
		progressDialog = new ProgressDialog(this);
		progressDialog.setMessage(getString(R.string.encoding));
		progressDialog.setIndeterminate(false);
		progressDialog.setMax(100);
		progressDialog.setCanceledOnTouchOutside(false);
		progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
		progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel",
				new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int which) {
						docode.cancel(true);
						dialog.dismiss();
					}
				});
		progressDialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
			public boolean onKey(DialogInterface dialog, int keyCode,
					KeyEvent event) {
				return true;
			}
		});
		docode.execute((String[]) null);
	}

	public void decode(View view) {
		String sourcePath = ((EditText) findViewById(R.id.editTextInputFile))
				.getText().toString();
		String derivedPath = ((EditText) findViewById(R.id.editTextDestinationDirectory))
				.getText().toString();
		if (null == sourcePath || null == derivedPath
				|| sourcePath.length() == 0 || derivedPath.length() == 0) {
			Toast.makeText(getApplicationContext(),
					getString(R.string.chooseSrcDests), Toast.LENGTH_SHORT)
					.show();
			return;
		}
		File sourceFile = new File(sourcePath);
		if ((sourceFile.length() * (long) 2) > DocryptUtil.getSpaceAvailable()) {
			Toast.makeText(getApplicationContext(),
					getString(R.string.spaceNotAvailable), Toast.LENGTH_SHORT)
					.show();
			return;
		}
		String sourceFileName = sourceFile.getName();
		int lastIndex = sourceFileName.lastIndexOf(".");
		String derivedFileName = null;
		if (lastIndex > 0) {
			derivedFileName = sourceFileName.substring(0, lastIndex);
		} else {
			derivedFileName = DECODED_FILE_NAME;
		}
		File derivedFile = new File(derivedPath + FILE_SEPARATOR
				+ derivedFileName);
		EncodingType encodingType = EncodingType
				.forName(((Spinner) findViewById(R.id.spinnerEncodingMethods))
						.getSelectedItem().toString());
		final Docode docode = new Docode(sourceFile, derivedFile, encodingType,
				false);
		progressDialog = new ProgressDialog(this);
		progressDialog.setMessage(getString(R.string.decoding));
		progressDialog.setIndeterminate(false);
		progressDialog.setMax(100);
		progressDialog.setCanceledOnTouchOutside(false);
		progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
		progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel",
				new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int which) {
						docode.cancel(true);
						dialog.dismiss();
					}
				});
		progressDialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
			public boolean onKey(DialogInterface dialog, int keyCode,
					KeyEvent event) {
				return true;
			}
		});
		docode.execute((String[]) null);
	}

	private class Docode extends AsyncTask<String, Integer, String> {
		private File sourceFile;
		private File derivedFile;
		private EncodingType encodingType;
		private boolean doEncode;

		public Docode(File sourceFile, File derivedFile,
				EncodingType encodingType, boolean doEncode) {
			this.sourceFile = sourceFile;
			this.derivedFile = derivedFile;
			this.encodingType = encodingType;
			this.doEncode = doEncode;
		}

		@Override
		protected String doInBackground(String... strings) {
			if (doEncode) {
				encode(this.encodingType, this.sourceFile, this.derivedFile);
			} else {
				decode(this.encodingType, this.sourceFile, this.derivedFile);
			}
			return null;
		}

		public void encode(EncodingType encodingType, File sourceFile,
				File derivedFile) {
			BufferedWriter bufferedWriter = null;
			FileInputStream fileInputStream = null;
			try {
				FileWriter fileWriter = new FileWriter(derivedFile);
				bufferedWriter = new BufferedWriter(fileWriter);
				fileInputStream = new FileInputStream(sourceFile);
				FileChannel fileChannel = fileInputStream.getChannel();
				long fileLength = fileChannel.size();
				MappedByteBuffer mappedByteBuffer = fileChannel.map(
						MapMode.READ_ONLY, 0L, fileLength);
				int BUFFER_SIZE = encodingType.getBufferSize();
				byte[] byteArray = new byte[BUFFER_SIZE];
				int nGet;
				long total = 0;
				while (mappedByteBuffer.hasRemaining()) {
					if (isCancelled())
						break;
					nGet = Math.min(mappedByteBuffer.remaining(), BUFFER_SIZE);
					mappedByteBuffer.get(byteArray, 0, nGet);
					if (nGet < BUFFER_SIZE) {
						byte[] newByteArray = new byte[nGet];
						System.arraycopy(byteArray, 0, newByteArray, 0, nGet);
						bufferedWriter.write(BaseCoder.encodeToString(
								encodingType, newByteArray));
					} else {
						bufferedWriter.write(BaseCoder.encodeToString(
								encodingType, byteArray));
					}
					bufferedWriter.newLine();
					total += nGet;
					publishProgress((int) (total * 100 / fileLength));
				}
			} catch (Exception e) {
			} finally {
				try {
					bufferedWriter.flush();
					bufferedWriter.close();
					fileInputStream.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}

		public void decode(EncodingType encodingType, File sourceFile,
				File derivedFile) {
			BufferedReader bufferedReader = null;
			BufferedOutputStream bufferedOutputStream = null;
			try {
				FileReader fileReader = new FileReader(sourceFile);
				bufferedReader = new BufferedReader(fileReader);
				FileOutputStream fileOutputStream = new FileOutputStream(
						derivedFile, true);
				bufferedOutputStream = new BufferedOutputStream(
						fileOutputStream);
				long fileLength = sourceFile.length();
				long total = 0;
				String line;
				while ((line = bufferedReader.readLine()) != null) {
					if (isCancelled())
						break;
					byte[] byteArray = BaseCoder.decodeToBytes(encodingType,
							line);
					bufferedOutputStream.write(byteArray);
					total += byteArray.length;
					publishProgress((int) (total * encodingType.getMultiplier() / fileLength));
				}
				publishProgress(100);
			} catch (Exception e) {
			} finally {
				try {
					bufferedReader.close();
					bufferedOutputStream.flush();
					bufferedOutputStream.close();
				} catch (Exception e) {
				}
			}
		}

		@Override
		protected void onPreExecute() {
			super.onPreExecute();
			progressDialog.show();
		}

		@Override
		protected void onProgressUpdate(Integer... progress) {
			super.onProgressUpdate(progress);
			progressDialog.setProgress(progress[0]);
		}

		@Override
		protected void onPostExecute(String result) {
			super.onPostExecute(result);
			if (((CheckBox) findViewById(R.id.checkBoxDeleteSourceOnCompletion))
					.isChecked()) {
				if (sourceFile.delete()) {
					Toast.makeText(
							getApplicationContext(),
							getString(R.string.processCompletedAndSourceDeleted),
							Toast.LENGTH_LONG).show();
				} else {
					Toast.makeText(
							getApplicationContext(),
							getString(R.string.processCompletedAndSourceNotDeleted),
							Toast.LENGTH_LONG).show();
				}
			} else {
				if (doEncode) {
					Toast.makeText(getApplicationContext(),
							getString(R.string.encodeComplete), Toast.LENGTH_SHORT)
							.show();
				} else {
					Toast.makeText(getApplicationContext(),
							getString(R.string.decodeComplete), Toast.LENGTH_SHORT)
							.show();
				}
			}
			progressDialog.dismiss();
		}

		@Override
		protected void onCancelled() {
			super.onCancelled();
			if (derivedFile.delete()) {
				Toast.makeText(getApplicationContext(),
						getString(R.string.processCancelled), Toast.LENGTH_LONG)
						.show();
			} else {
				Toast.makeText(
						getApplicationContext(),
						getString(R.string.processCancelledAndOutputFileNotDeleted),
						Toast.LENGTH_LONG).show();
			}
		}
	}
}
